package pers.cz.config;

import cn.hutool.core.date.DateUtil;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.ResourceCDN;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.model.TestAttribute;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.ChartLocation;
import com.aventstack.extentreports.reporter.configuration.Theme;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pers.cz.io.IOUtils;
import pers.cz.mapper.TaskJobReportMapper;
import pers.cz.mapper.TestCaseMapper;
import pers.cz.model.TaskJobReport;
import pers.cz.model.TestCase;
import pers.cz.pub.BeanHelper;
import pers.cz.pub.CpublicFunc;
import pers.cz.pub.SequenceEnum;
import pers.cz.spring.SpringContextUtil;
import org.testng.*;
import org.testng.xml.XmlSuite;

import java.io.*;
import java.util.*;

/**
 * 测试报告格式
 * @program: PostGril_1.0
 * @description: ExtentTestNGIReporterListener
 * @author: Cheng Zhi
 * @create: 2021-02-23 11:23
 **/
public class ExtentTestNGIReporterListener implements IReporter {

    static Logger logger = LoggerFactory.getLogger(ExtentTestNGIReporterListener.class);

    protected BeanHelper daoHelper;
    //生成的路径以及文件名
    private static final String OUTPUT_FOLDER = "report/";
    private static String FILE_NAME = "test.html";
    private static final String FIlE_NAME_SECQUENCE = "FIlE_NAME_SECQUENCE";

    private ExtentReports extent;
    private boolean isJob = false;
    private Long jobSonbr;

    /**
     * job调度使用
     * @param jobSonbr
     */
    public ExtentTestNGIReporterListener(Long jobSonbr) {
        this.jobSonbr = jobSonbr;
        isJob = true;
    }

    /**
     * 无参构造器
     */
    public ExtentTestNGIReporterListener() {

    }

    @Override
    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
        daoHelper = SpringContextUtil.getBean(BeanHelper.class);
        CpublicFunc cpublicFunc  = new CpublicFunc(daoHelper);
        Long fileName = cpublicFunc.getSoNbr(SequenceEnum.FIlE_NAME_SECQUENCE);
        FILE_NAME = String.valueOf(fileName) + ".html";

        if (isJob) {
            updateSystemJobLog(FILE_NAME, jobSonbr);
        } else {

        }
        init();
        boolean createSuiteNode = false;
        if(suites.size()>1){
            createSuiteNode=true;
        }
        for (ISuite suite : suites) {
            Map<String, ISuiteResult> result = suite.getResults();
            //如果suite里面没有任何用例，直接跳过，不在报告里生成
            if(result.size()==0){
                continue;
            }
            //统计suite下的成功、失败、跳过的总用例数
            int suiteFailSize=0;
            int suitePassSize=0;
            int suiteSkipSize=0;
            ExtentTest suiteTest=null;
            //存在多个suite的情况下，在报告中将同一个一个suite的测试结果归为一类，创建一级节点。
            if(createSuiteNode){
                suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName());
            }
            boolean createSuiteResultNode = false;
            if(result.size()>1){
                createSuiteResultNode=true;
            }
            for (ISuiteResult r : result.values()) {
                ExtentTest resultNode;
                ITestContext context = r.getTestContext();
                if(createSuiteResultNode){
                    //没有创建suite的情况下，将在SuiteResult的创建为一级节点，否则创建为suite的一个子节点。
                    if( null == suiteTest){
                        resultNode = extent.createTest(r.getTestContext().getName());
                    }else{
                        resultNode = suiteTest.createNode(r.getTestContext().getName());
                    }
                }else{
                    resultNode = suiteTest;
                }
                if(resultNode != null){
                    resultNode.getModel().setName(suite.getName()+" : "+r.getTestContext().getName());
                    if(resultNode.getModel().hasCategory()){
                        resultNode.assignCategory(r.getTestContext().getName());
                    }else{
                        resultNode.assignCategory(suite.getName(),r.getTestContext().getName());
                    }
                    resultNode.getModel().setStartTime(r.getTestContext().getStartDate());
                    resultNode.getModel().setEndTime(r.getTestContext().getEndDate());
                    // 统计SuiteResult下的数据
                    int passSize = r.getTestContext().getPassedTests().size();
                    int failSize = r.getTestContext().getFailedTests().size();
                    int skipSize = r.getTestContext().getSkippedTests().size();
                    suitePassSize += passSize;
                    suiteFailSize += failSize;
                    suiteSkipSize += skipSize;
                    if(failSize>0){
                        resultNode.getModel().setStatus(Status.FAIL);
                    }
                    resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",passSize,failSize,skipSize));
                }

                try {
                    buildTestNodes(resultNode,context.getPassedTests(), Status.PASS);
                    buildTestNodes(resultNode,context.getFailedTests(), Status.FAIL);
                    buildTestNodes(resultNode,context.getSkippedTests(), Status.SKIP);
                } catch (Exception e) {
                    logger.error("创建展示节点异常", e);
                }

            }
            if(suiteTest!= null){
                suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",suitePassSize,suiteFailSize,suiteSkipSize));
                if(suiteFailSize>0){
                    suiteTest.getModel().setStatus(Status.FAIL);
                }
            }
        }
        extent.flush();
        // 修改html文件中的部分内容
        changeContent(OUTPUT_FOLDER + FILE_NAME,OUTPUT_FOLDER + FILE_NAME);
    }

    private void init() {
        //文件夹不存在的话进行创建
        File reportDir= new File(OUTPUT_FOLDER);
        if(!reportDir.exists()&& !reportDir .isDirectory()){
            reportDir.mkdir();
        }
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
        // 设置静态文件的DNS
        //怎么样解决cdn.rawgit.com访问不了的情况
        htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);

        htmlReporter.config().setDocumentTitle("PostGirl自动化回归测试报告");
        htmlReporter.config().setReportName("PostGirl自动化回归测试报告");
        htmlReporter.config().setChartVisibilityOnOpen(true);
        //htmlReporter.config().setEncoding("GB18030");
        //htmlReporter.config().setEncoding("UTF-8");
        htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setCSS(".node.level-1  ul{ display:none;} .node.level-1.active ul{display:block;}");

        // 自定义配置信息
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        extent.setReportUsesManualConfiguration(true);
    }

    private void buildTestNodes(ExtentTest extenttest, IResultMap tests, Status status) {
        //存在父节点时，获取父节点的标签
        String[] categories=new String[0];
        if(extenttest != null ){
            List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll();
            categories = new String[categoryList.size()];
            for(int index=0;index<categoryList.size();index++){
                categories[index] = categoryList.get(index).getName();
            }
        }

        ExtentTest test;

        if (tests.size() > 0) {
            //调整用例排序，按时间排序
            Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {
                @Override
                public int compare(ITestResult o1, ITestResult o2) {
                    return o1.getStartMillis()<o2.getStartMillis()?-1:1;
                }
            });
            treeSet.addAll(tests.getAllResults());
            for (ITestResult result : treeSet) {
                Object[] parameters = result.getParameters();
                String name="";
                //如果有参数，则使用参数的toString组合代替报告中的name
                // String request, String response, String url, String id, String ignoreFields
                String request = parameters[0].toString();
                String response = parameters[1].toString();
                name = parameters[5].toString();
                String id = parameters[3].toString();

                if (!isJob) {
                    // 如果不是job调用，则是普通测试，需要更新单个测试案例对应的测试报告。
                    this.updateTestCase(FILE_NAME,id);
                }

                if(extenttest==null){
                    test = extent.createTest(name);
                }else{
                    //作为子节点进行创建时，设置同父节点的标签一致，便于报告检索。
                    test = extenttest.createNode(name).assignCategory(categories);
                }
                for (String group : result.getMethod().getGroups())
                    test.assignCategory(group);

                List<String> outputList = Reporter.getOutput(result);
                for(String output:outputList){
                    //将用例的log输出报告中
                    test.debug(output);
                }
                if (result.getThrowable() != null) {
                    test.log(status, result.getThrowable());
                } else {
                    test.log(status, "Test " + status.toString().toLowerCase() + "ed");
                }

                test.getModel().setStartTime(getTime(result.getStartMillis()));
                test.getModel().setEndTime(getTime(result.getEndMillis()));
            }
        }
    }

    private Date getTime(long millis) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        return calendar.getTime();
    }

    /**
     * 将测试报告文件名更新到表中
     * @param fileName
     * @param id
     */
    public void updateTestCase(String fileName, String id) {

        TestCase testCase = new TestCase();
        testCase.setId(Integer.valueOf(id));
        testCase.setTestResultFileName(fileName);
        SpringContextUtil.getBean(TestCaseMapper.class).updateByPrimaryKeySelective(testCase);
    }

    /**
     * 更新定时调度测试案例报告
     * @param fileName
     * @param id
     */
    public void updateSystemJobLog(String fileName, Long id) {

        TaskJobReport taskJobReport = new TaskJobReport();
        taskJobReport.setId(id);
        taskJobReport.setFileName(fileName);
        taskJobReport.setCreatedTime(DateUtil.date());

        SpringContextUtil.getBean(TaskJobReportMapper.class).insert(taskJobReport);
    }

    /**
     * 更新html测试报告中的css样式地址
     * @param filePath
     * @param savePath
     */
    private static void changeContent(String filePath, String savePath) {
        // filePath 要读取的文件 savePath 要写入的文件
        BufferedReader br = null;
        BufferedWriter bw = null;
        savePath = savePath + "_BAK";
        try {
            // 以下读取和写入都转成UTF-8 防止乱码
            br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"));
            bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(savePath), "UTF-8"));
            String line = null;
            while ((line = br.readLine()) != null && (line != "")) {
                // 替换html文件中的这个链接为本地的resource文件夹路径
                bw.write(line.replaceAll("http://extentreports.com/resx/dist/", "http://10.10.152.228:8001/static/"));
            }
            bw.flush();
        } catch (Exception e) {
            logger.error("更新HTML样式失败", e);
        } finally {
            IOUtils.close(br);
            IOUtils.close(bw);
        }

        // 删除老文件，并重命名文件
        new File(filePath).delete();

        File file = new File(filePath);
        new File(savePath).renameTo(file);
    }
}

